from mutation import default_mutation,default_crossover,default_random
from nsga2 import nsga2
from random import shuffle
from multiprocessing import cpu_count
from subprocess import Popen,PIPE,STDOUT
from pickle import dump,load

def create_children(parent_pop,p_min,p_max,p_ro,mutation_operator,crossover_operator):
  assert len( parent_pop )%2 == 0
  child_pop = []
  shuffle( parent_pop )
  for i in range(len(parent_pop)/2):
    a,b=crossover_operator(parent_pop[i],parent_pop[i+(len( parent_pop)/2)])
    child_pop.append( mutation_operator(a,p_min,p_max,p_ro))
    child_pop.append( mutation_operator(b,p_min,p_max,p_ro))
  return child_pop



def loop(f, ea, parent_pop, child_pop,gen):
  assert len(parent_pop)==len(child_pop)
  test_pop=[element[0] for element in child_pop]
  rest_pop=[]
  for element in parent_pop:
    if len(element)==1:
      test_pop.append(element[0])
    else:
      assert(len(element)==2)
      rest_pop.append(element)
  fname="gen_"+str(gen)
  pop_file=open(fname,"w")
  dump(test_pop,pop_file)
  pop_file.close()
### exec subprocess for fitness ####
  Popen(["python",f,fname],shell=False,stdout=None,stderr=None).wait()
### end subprocess
  file=open(fname,"r")
  fitness=load(file)
  r_t = rest_pop + fitness
# r_t = [(element[0],(f(element[0]))) if len(element)==1 else element for element in parent_pop + child_pop] 
  new_pop = ea(r_t)
  assert(len(new_pop)==len(parent_pop))
  return new_pop

## f : objective function file, should follow template, it gives the objectives for a given population, to be maximised
## p_min and p_max : min and max values if the default mutation operator is used (gaussian mutation process), if a custom mutation operator is defined, can be used to give parameters
## p_init : initial population if available
## ea : evolutionary algorithm, takes the population and tuples of results, output the selected individuals
## mutation_operator : a mutation operator which creates an individual from another
## crossover_operator : a crossover operator which crosses two individuals and outputs two offsprings a,b as a tuple or list
## pop_size : number of individuals in the population, must be a pair number
## initial_pop_multiplier : number of individuals in the population, must be a pair number

def evolve(f, p_min, p_max,p_ro=None, p_init=None, ea=nsga2, random_operator=default_random, mutation_operator=default_mutation,crossover_operator=default_crossover,generations=100,pop_size=200,initial_pop_multiplier=3):
  if p_init==None:
    parent_pop=[[random_operator(p_min,p_max)] for i in range( initial_pop_multiplier * pop_size )]
  else:
    parent_pop=[[indiv] for indiv in p_init]
  for gen in range(generations):
    print gen
#remove the objective when creating children
    parent_pop=loop(f,ea,parent_pop,create_children([[element[0]] if len(element)>1 else element for element in parent_pop],p_min,p_max,p_ro,mutation_operator,crossover_operator),gen)
  return parent_pop
